﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using GBricks.Properties;

namespace GBricks.Collections
{
  [Serializable]
  [DebuggerDisplay("Count = {Count}"), DebuggerTypeProxy("System.Collections.Generic.Mscorlib_CollectionDebugView`2, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
  public class ReadOnlySet<T> : ISet<T>, IReadOnlyCollection<T>
  {
    public ReadOnlySet(ISet<T> items) {
      Argument.NotNull(items, "items");
      Items = items;
      Contract.Assert(Items.Count == Count);
    }

    protected ISet<T> Items { get; private set; }

    protected Exception CreateCollectionIsReadOnlyException() {
      Contract.Ensures(Contract.Result<Exception>() != null);
      return ExceptionBuilder.NotSupported(Resources.CollectionIsReadOnly);
    }

    [ContractInvariantMethod]
    private void Invariant() {
      Contract.Invariant(Items != null);
      Contract.Invariant(Items.Count == Count);
    }

    #region ISet<TItem> Members

    bool ISet<T>.Add(T item) {
      throw CreateCollectionIsReadOnlyException();
    }

    void ISet<T>.ExceptWith(IEnumerable<T> other) {
      throw CreateCollectionIsReadOnlyException();
    }

    void ISet<T>.IntersectWith(IEnumerable<T> other) {
      throw CreateCollectionIsReadOnlyException();
    }

    void ISet<T>.SymmetricExceptWith(IEnumerable<T> other) {
      throw CreateCollectionIsReadOnlyException();
    }

    void ISet<T>.UnionWith(IEnumerable<T> other) {
      throw CreateCollectionIsReadOnlyException();
    }

    [Pure]
    public bool IsProperSubsetOf(IEnumerable<T> other) {
      return Items.IsProperSubsetOf(other);
    }

    [Pure]
    public bool IsProperSupersetOf(IEnumerable<T> other) {
      return Items.IsProperSupersetOf(other);
    }

    [Pure]
    public bool IsSubsetOf(IEnumerable<T> other) {
      return Items.IsSubsetOf(other);
    }

    [Pure]
    public bool IsSupersetOf(IEnumerable<T> other) {
      return Items.IsSupersetOf(other);
    }

    [Pure]
    public bool Overlaps(IEnumerable<T> other) {
      return Items.Overlaps(other);
    }

    [Pure]
    public bool SetEquals(IEnumerable<T> other) {
      return Items.SetEquals(other);
    }

    #endregion ISet<TItem> Members

    #region IReadOnlyCollection<T> Members

    public int Count {
      [DebuggerStepThrough]
      get {
        Contract.Ensures(Contract.Result<int>() >= 0);
        return Items.Count;
      }
    }

    #endregion IReadOnlyCollection<T> Members

    #region ICollection<T> Members

    bool ICollection<T>.IsReadOnly {
      [DebuggerStepThrough]
      get { return true; }
    }

    void ICollection<T>.Add(T item) {
      throw CreateCollectionIsReadOnlyException();
    }

    void ICollection<T>.Clear() {
      throw CreateCollectionIsReadOnlyException();
    }

    [Pure]
    public bool Contains(T item) {
      Contract.Ensures(!Contract.Result<bool>() || Count > 0);
      return Items.Contains(item);
    }

    [Pure]
    public void CopyTo(T[] array, int arrayIndex) {
      Argument.NotNull(array, "array");
      Argument.OutOfRange(arrayIndex >= 0, "arrayIndex");
      Argument.OutOfRange(arrayIndex <= array.Length - Count, "arrayIndex");
      Items.CopyTo(array, arrayIndex);
      Contract.Assert(Items.Count == Count);
    }

    bool ICollection<T>.Remove(T item) {
      throw CreateCollectionIsReadOnlyException();
    }

    #endregion ICollection<T> Members

    #region IEnumerable<T> Members

    [DebuggerStepThrough]
    [Pure]
    public IEnumerator<T> GetEnumerator() {
      Contract.Ensures(Contract.Result<IEnumerator<T>>() != null);
      return Items.GetEnumerator();
    }

    #endregion IEnumerable<T> Members

    #region IEnumerable Members

    [DebuggerStepThrough]
    IEnumerator IEnumerable.GetEnumerator() {
      Contract.Ensures(Contract.Result<IEnumerator>() != null);
      return GetEnumerator();
    }

    #endregion IEnumerable Members
  }
}
